#include <unistd.h>
#include <cstdint>
#include <functional>
#include <vector>
#include <unordered_map>
#include <memory>
#include <iostream>

using TaskFunc = std::function<void()>;
using ReleaseFunc = std::function<void()>;
class TimerTask
{
private:
    uint64_t _id;   //定时器任务对象ID  定时任务必须得找得着，一个程序里定时任务可能有很多
    uint32_t _timeout;  //定时任务的超时时间
    TaskFunc _task_cb;  //定时器要执行的任务
    //用于删除TimerWheel中保存的定时器任务对象信息，定时任务释放的时候也要清理TimerWheel中保存的定时器对象信息
    //为什么将这个_release设置到TimerTask里面呢，不在TimerWheel层管理？
    //因为这个TimerWheel不知道是否某个定时任务真的释放了，而TimerTask是最清楚的，自己真的释放了就会调用析构函数
    ReleaseFunc _release;
    bool _canceled;     //false - 代表没有被取消，true - 代表取消了
public:
    TimerTask(uint64_t id, uint32_t timeout, const TaskFunc& cb)
    :_id(id), _timeout(timeout), _task_cb(cb), _canceled(false)
    {}
    ~TimerTask()
    {
        if (_canceled == false) //如果定时任务没有被取消
            _task_cb();
        _release();
    }
    void SetRelease(const ReleaseFunc& cb)
    {
        _release = cb;
    }
    uint32_t DelayTime()
    {
        return _timeout;
    }
    void Cancel()
    {
        _canceled = true;
    }
};

class TimerWheel
{
private:
    using WeakTask = std::weak_ptr<TimerTask>;
    using PtrTask = std::shared_ptr<TimerTask>;
    std::vector<std::vector<PtrTask>> _wheel;
    int _tick;      //当前的秒针，走到哪里释放哪里，就相当于执行哪里的任务
    int _capacity;  //表盘最大数量 -- 也是最大能设置的延时时间
    //为什么不用普通指针，要用weak_ptr，因为刷新定时任务的时候，需要通过该weak_ptr找到曾经shared_ptr，而普通指针则不行
    std::unordered_map<uint64_t, WeakTask> _timers;     //对所有的定时任务进行管理 
private:
    void RemoveTimer(uint64_t id)
    {
        auto pos = _timers.find(id);
        if (pos != _timers.end())
        {
            _timers.erase(pos);
        }
    }
public:
    TimerWheel():_capacity(60), _tick(0), _wheel(_capacity)
    {}
    //时间轮提供了一个功能：释放定时任务的功能
    //至于释放的任务是什么，这个组件也不知道，需要上层对内 设置回调函数
    void TimerAdd(uint64_t id, uint32_t delay, const TaskFunc& cb) //添加定时任务
    {
        //1.构建定时任务
        PtrTask pt(new TimerTask(id, delay, cb));
        pt->SetRelease(std::bind(&TimerWheel::RemoveTimer, this, id));
        //2.将定时任务加入到_wheel中
        int i = (_tick + delay) % _capacity;
        _wheel[i].push_back(pt);
        //3.加入到时间轮的_timers里面
        // std::unordered_map<uint64_t, WeakTask>::iterator pos = _timers.find(id);
        auto pos = _timers.find(id);
        if (pos == _timers.end())
        {
            _timers.insert({id, pt});
        }
    }
    void TimerRefresh(uint64_t id)  //刷新/延迟定时任务
    {
        //通过id找到对应的定时任务
        auto pos = _timers.find(id);
        if (pos == _timers.end())
        {
            //如果没找到定时任务，则没办法更新
            return;
        }
        //获取到对应定时任务的shared_ptr，并构建一个新的智能指针，对应的计数加1
        PtrTask pt = pos->second.lock();
        //将对应的pt加入到_wheel中
        int delay = pt->DelayTime();
        int i = (_tick + delay) % _capacity;
        _wheel[i].push_back(pt);
    }
    //这个函数会每秒钟执行一次，相当于秒针向后走了一步
    void RunTimerTask()
    {
        _tick = (_tick + 1) % _capacity;
        _wheel[_tick].clear();  //情况指定位置的数组，就会把数组中保存的所有管理定时器对象的shared_ptr释放掉
    }
    void TimerCancel(uint64_t id)
    {
        auto pos = _timers.find(id);
        if (pos == _timers.end())
        {
            //没找到定时任务，没法刷新，没法延时
            return;
        }
        PtrTask pt = pos->second.lock();
        pt->Cancel();
    }
};

//该Test类用于测试，能清楚的观察到过程
struct Test
{
    Test()
    {
        std::cout << "Test()" << std::endl;
    }
    ~Test()
    {
        std::cout << "~Test()" << std::endl;
    }
};

//要交给时间轮的定时任务
void DelTest(Test* t)
{
    delete t;
}

int main()
{
    TimerWheel tw;
    Test* t = new Test();
    tw.TimerAdd(888, 5, std::bind(DelTest, t));
    for (int i = 0; i < 8; ++i)
    {
        sleep(1);
        tw.RunTimerTask();  //硬编码模拟时间轮走动
        tw.TimerRefresh(888);  //每一秒都刷新定时任务
        std::cout << "走了：" << i + 1 << "秒" << std::endl;
    }
    tw.TimerCancel(888);
    while (1)
    {
        sleep(1);
        std::cout << "----------------------" << std::endl;
        tw.RunTimerTask();
    }
    return 0;
}